﻿using NHibernate;
using NHibernate.Linq;
using Romantics.Domain.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Romantics.Domain
{
    public class DomainRepository : IDomainRepository
    {
        private readonly ISession _session;

        public DomainRepository(ISession session)
        {
            _session = session;
        }

        public int Total<U>() where U : Entity { return _session.Query<U>().Count(); }

        public int  Add<U>(U entity) where U : Entity
        {
            using (var tran = _session.BeginTransaction())
            {
                _session.Save(entity);
                tran.Commit();
                return entity.ID;
            }
        }

        public void Edit<U>(U entity) where U : Entity
        {
            using (var tran = _session.BeginTransaction())
            {
                _session.Update(entity);
                tran.Commit();
            }
        }

        public void Delete<U>(int id) where U : Entity
        {
            using (var tran = _session.BeginTransaction())
            {
                U entity = _session.Get<U>(id);
                if(entity != null)
                    _session.Delete(entity);
                tran.Commit();
            }
        }

        #region Country

        public IList<Country> Countries(int pageNo, int pageSize, string sortColumn, bool sortByAscending)
        {
            IQueryable<Country> query = _session.Query<Country>();

            switch (sortColumn)
            {
                case "Name":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.Name);
                    else
                        query = query.OrderByDescending(h => h.Name);
                    break;

                default:
                    query = query.OrderByDescending(h => h.URL);
                    break;
            }
            query = query.Skip(pageNo * pageSize)
                        .Take(pageSize)
                        ;

            return query.ToFuture().ToList();
        }

        #endregion

        #region Hike

        public IList<Hike> Hikes(int pageNo, int pageSize, string sortColumn, bool sortByAscending, string name = null, string category = null, string region = null)
        {
            IQueryable<Hike> query = _session.Query<Hike>();

            if (category != null)
                query = query.Where(h => h.Category.Name.ToLower().Contains(category.ToLower()));
            if (region != null)
                query = query.Where(h => h.Region.Name.ToLower().Contains(region.ToLower()));
            if (name != null)
                query = query.Where(h => h.Name.ToLower().Contains(name.ToLower()));
            switch (sortColumn)
            {
                case "Name":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.Name);
                    else
                        query = query.OrderByDescending(h => h.Name);
                    break;

                case "Published":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.Published);
                    else
                        query = query.OrderByDescending(h => h.Published);
                    break;

                case "StartDate":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.StartDate);
                    else
                        query = query.OrderByDescending(h => h.StartDate);
                    break;

                case "EndDate":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.EndDate);
                    else
                        query = query.OrderByDescending(h => h.EndDate);
                    break;

                case "Region":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.Region.Name);
                    else
                        query = query.OrderByDescending(h => h.Region.Name);
                    break;

                case "Category":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.Category.Name);
                    else
                        query = query.OrderByDescending(h => h.Category.Name);
                    break;

                default:
                    query = query.OrderByDescending(h => h.StartDate);
                    break;
            }

            query = query.Skip(pageNo * pageSize)
                        .Take(pageSize)
                        .Fetch(h => h.Category)
                        .Fetch(h => h.Region)
                        .ThenFetch(r => r.Country)
                        ;

            return query.ToFuture().ToList();
        }

        public IList<Hike> PlannedHikes(int pageNo, int pageSize, Category category = null, Region region = null)
        {
            var query = _session.Query<Hike>()
                .Where(h => h.StartDate > DateTime.Now.Date)
                .Where(h => h.Published);
            if (category != null)
                query = query.Where(h => h.Category == category);
            if (region != null)
                query = query.Where(h => h.Region == region);
            query = query.OrderBy(h => h.StartDate)
                            .Skip(pageNo * pageSize)
                            .Take(pageSize);
            return query.ToFuture().ToList();
        }

        public int TotalPlannedHikes(Category category, Region region = null)
        {
            var query = _session.Query<Hike>()
                .Where(h => h.StartDate > DateTime.Now.Date)
                .Where(h => h.Published);
            if (region != null)
                query = query.Where(h => h.Region == region);
            if (category != null)
                query = query.Where(h => h.Category == category);
            return query.Count();
        }

        public IList<Hike> PreviousHikes(int pageNo, int pageSize, int? year, Category category = null, Region region = null)
        {
            var query = _session.Query<Hike>()
                .Where(h => h.StartDate < DateTime.Now.Date)
                .Where(h => h.Published);
            if (year.HasValue)
                query = query.Where(h => h.StartDate.Year == year.Value);
            if (category != null)
                query = query.Where(h => h.Category == category);
            if (region != null)
                query = query.Where(h => h.Region == region);
            query = query
                .OrderBy(h => h.StartDate)
                .Skip(pageNo * pageSize)
                .Take(pageSize);
            return query.ToFuture().ToList();
        }

        public Hike Hike(int id)
        {
            var query = _session.Query<Hike>()
                .Where(h => h.ID == id)
                .Where(h => h.Published);
            query.FetchMany(h => h.PhotoAlbums).ToFuture();
            query.FetchMany(h => h.Reports).ToFuture();

            return query.SingleOrDefault();
        }

        public int TotalPreviousHikes(int? year, Category category, Region region = null)
        {
            var query = _session.Query<Hike>()
                .Where(h => h.StartDate < DateTime.Now.Date)
                .Where(h => h.Published);
            if (year.HasValue)
                query = query.Where(h => h.StartDate.Year == year.Value);
            if (region != null)
                query = query.Where(h => h.Region == region);
            if (category != null)
                query = query.Where(h => h.Category == category);
            return query.Count();
        }

        #endregion

        #region Category

        public Category Category(string category_url)
        {
            var query = _session.Query<Category>()
                .Where(c => c.URL.ToLower() == category_url.ToLower());
            return query.ToFuture().FirstOrDefault();
        }

        public Category Category(int id)
        {
            var query = _session.Query<Category>()
                .Where(c => c.ID == id);
            return query.ToFuture().FirstOrDefault();
        }

        public IList<Category> Categories()
        {
            var query = _session.Query<Category>()
                .OrderBy(c => c.Name);
            return query.ToFuture().ToList();
        }

        #endregion

        #region Region

        public Region Region(string region_url)
        {
            var query = _session.Query<Region>()
                .Where(r => r.URL.ToLower() == region_url.ToLower());
            return query.ToFuture().FirstOrDefault();
        }

        public Region Region(int id)
        {
            var query = _session.Query<Region>()
                .Where(c => c.ID == id);
            return query.ToFuture().FirstOrDefault();
        }

        public IList<Region> Regions(int pageNo, int pageSize, string sortColumn, bool sortByAscending, string name = null, string country = null)
        {
            IQueryable<Region> query = _session.Query<Region>();
            if (country != null)
                query = query.Where(h => h.Country.Name.ToLower().Contains(country.ToLower()));
            if (name != null)
                query = query.Where(h => h.Name.ToLower().Contains(name.ToLower()));
            switch (sortColumn)
            {
                case "Name":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.Name);
                    else
                        query = query.OrderByDescending(h => h.Name);
                    break;

                case "Country":
                    if (sortByAscending)
                        query = query
                            .OrderBy(h => h.Country.Name)
                            .ThenBy(h => h.Name);
                    else
                        query = query
                            .OrderByDescending(h => h.Country.Name)
                            .ThenByDescending(h => h.Name);;
                    break;

                default:
                    query = query.OrderByDescending(h => h.URL);
                    break;
            }
            query = query.Skip(pageNo * pageSize)
                        .Take(pageSize)
                        .Fetch(r => r.Country)
                        ;

            return query.ToFuture().ToList();
        }

        public IList<int> Years()
        {
            var query = _session.Query<Hike>()
                .Where(h => h.StartDate.Year <= DateTime.Now.Year)
                .Select(h => h.StartDate.Year)
                .Distinct();
            List<int> result = query.ToList();
            result.Sort();
            return result;
        }

        #endregion

        #region PhotoAlbum

        public PhotoAlbumItem PhotoAlbumItem(int id)
        {
            var query = _session.Query<PhotoAlbumItem>()
               .Where(a => a.ID == id);
            return query.SingleOrDefault();
        }
        public IList<PhotoAlbumSummary> PhotoAlbumSummary(int pageNo, int pageSize, string sortColumn, bool sortByAscending, string name = null, string hike = null)
        {
            IQueryable<PhotoAlbumSummary> query = _session.Query<PhotoAlbumSummary>();

            if (hike != null)
                query = query.Where(h => h.Hike.Name.ToLower().Contains(hike.ToLower()));
            if (name != null)
                query = query.Where(h => h.Name.ToLower().Contains(name.ToLower()));
            switch (sortColumn)
            {
                case "Name":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.Name);
                    else
                        query = query.OrderByDescending(h => h.Name);
                    break;

                case "Published":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.Published);
                    else
                        query = query.OrderByDescending(h => h.Published);
                    break;

                default:
                    query = query.OrderByDescending(h => h.Name);
                    break;
            }

            query = query.Skip(pageNo * pageSize)
                        .Take(pageSize)
                        .Fetch(h => h.Hike)
                        .ThenFetch(h => h.PhotoAlbums)
                        .Fetch(h => h.Hike)
                        .ThenFetch(h => h.Category)
                        .Fetch(h => h.Hike)
                        .ThenFetch(h => h.Region)
                        .ThenFetch(r => r.Country)
                        ;

            return query.ToFuture().ToList();
        }

        public IList<PhotoAlbum> PhotoAlbums(int pageNo, int pageSize, int? year, Category category = null, Region region = null)
        {
            var query = _session.Query<PhotoAlbum>()
                .Where(a => a.Hike.StartDate < DateTime.Now.Date);
            if (year.HasValue)
                query = query.Where(a => a.Hike.StartDate.Year == year.Value);
            if (category != null)
                query = query.Where(a => a.Hike.Category == category);
            if (region != null)
                query = query.Where(a => a.Hike.Region == region);
            query = query
                .OrderBy(a => a.Hike.StartDate)
                .Skip(pageNo * pageSize)
                .Take(pageSize);
            query.FetchMany(a => a.Items).ToFuture();
            return query.ToFuture().ToList();
        }

        public int TotalPhotoAlbums(int? year, Category category = null, Region region = null)
        {
            var query = _session.Query<PhotoAlbum>()
                .Where(a => a.Hike.StartDate < DateTime.Now.Date);
            if (year.HasValue)
                query = query.Where(a => a.Hike.StartDate.Year == year.Value);
            if (category != null)
                query = query.Where(a => a.Hike.Category == category);
            if (region != null)
                query = query.Where(a => a.Hike.Region == region);
            return query.Count();
        }

        public PhotoAlbum PhotoAlbum(int id)
        {
            var query = _session.Query<PhotoAlbum>()
                .Where(a => a.ID == id);
            query.FetchMany(a => a.Items).ToFuture();
            return query.SingleOrDefault();
        }

        public int PhotoAlbumItemsCount(int id)
        {
            var query = _session.Query<PhotoAlbumItem>()
                .Where(a => a.PhotoAlbum.ID == id);
            return query.Count();
        }

        #endregion

        #region Report

        public IList<Report> Reports(int pageNo, int pageSize, string sortColumn, bool sortByAscending, string name = null, string hike = null)
        {
            IQueryable<Report> query = _session.Query<Report>();

            if (hike != null)
                query = query.Where(h => h.Hike.Name.ToLower().Contains(hike.ToLower()));
            if (name != null)
                query = query.Where(h => h.Name.ToLower().Contains(name.ToLower()));
            switch (sortColumn)
            {
                case "Name":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.Name);
                    else
                        query = query.OrderByDescending(h => h.Name);
                    break;

                case "Published":
                    if (sortByAscending)
                        query = query.OrderBy(h => h.Published);
                    else
                        query = query.OrderByDescending(h => h.Published);
                    break;

                default:
                    query = query.OrderByDescending(h => h.Name);
                    break;
            }

            query = query.Skip(pageNo * pageSize)
                        .Take(pageSize)
                        .Fetch(h => h.Hike)
                        .ThenFetch(h=>h.PhotoAlbums)
                        .Fetch(h => h.Hike)
                        .ThenFetch(h => h.Category)
                        .Fetch(h => h.Hike)
                        .ThenFetch(h => h.Region)
                        .ThenFetch(r => r.Country)
                        ;

            return query.ToFuture().ToList();
        }

        public Report Report(int id)
        {
            var query = _session.Query<Report>()
                .Where(a => a.ID == id);
            return query.SingleOrDefault();
        }

        #endregion

        public Options Options()
        {
            var query = _session.Query<Options>()
                .Where(a => a.ID == 1);
            return query.FirstOrDefault();
        }

        public IList<Options> OptionList()
        {
            var query = _session.Query<Options>();
            return query.ToList();
        }
    }
}
